// Copyright 2008 web2me.dev
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package net.web2me.multiurl;

import java.util.ArrayList;
import java.util.List;

import net.web2me.api.CommonConstants;
import net.web2me.multiurl.InvalidPatternException;
import net.web2me.multiurl.MultiUrl;

/**
 * Provides helpers methods to work with {@link MultiUrl}
 * 
 * @author web2me
 * 
 */
public class MultiUrlUtil {

	private static final String CLOSE_BRACKET = "]";

	private static final String COMMA = ",";

	private static final String HYPHEN = "-";

	private static final String NON_NUMBER_REGEX = "\\D";

	private static final String DOLLAR = "$";

	private static final String ZERO = "0";

	private static final String OPEN_BRACKET = "[";

	private static final String OPEN_BRACKET_0 = "[0";

	/**
	 * Identifies a URL with a pattern and return the resulting URL's list.<br>
	 * The pattern starts when a opening bracket is found and ends when an
	 * ending bracket is found.<br>
	 * The pattern can be comma separated numbers, two numbers separated by a
	 * hyphen, or a mix of both.<br>
	 * In the first case, each number would be treated as is, picking them for
	 * resolving URL's.<br>
	 * <br>
	 * Example:
	 * 
	 * <pre>
	 * http://somegallery.com/gallery.php?gid=[109,115,143]
	 * </pre>
	 * 
	 * The resulting URL's list:
	 * 
	 * <pre>
	 * http://somegallery.com/gallery.php?gid=109
	 * http://somegallery.com/gallery.php?gid=115
	 * http://somegallery.com/gallery.php?gid=143
	 * </pre>
	 * 
	 * The second case, the numbers are used to calculate a range, going from
	 * the first one to the second one. <br>
	 * Example:
	 * 
	 * <pre>
	 * http://somegallery.com/gallery.php?gid=[100-102]
	 * </pre>
	 * 
	 * The resulting URL's list:
	 * 
	 * <pre>
	 * http://somegallery.com/gallery.php?gid=100
	 * http://somegallery.com/gallery.php?gid=101
	 * http://somegallery.com/gallery.php?gid=102
	 * </pre>
	 * 
	 * And there is also the possibility to combine both cases. <br>
	 * Example:
	 * 
	 * <pre>
	 * http://somegallery.com/gallery.php?gid=[100-102,107-110,150-152]
	 * </pre>
	 * 
	 * The resulting URL's list:
	 * 
	 * <pre>
	 * http://somegallery.com/gallery.php?gid=100
	 * http://somegallery.com/gallery.php?gid=101
	 * http://somegallery.com/gallery.php?gid=102
	 * http://somegallery.com/gallery.php?gid=107
	 * http://somegallery.com/gallery.php?gid=108
	 * http://somegallery.com/gallery.php?gid=109
	 * http://somegallery.com/gallery.php?gid=110
	 * http://somegallery.com/gallery.php?gid=150
	 * http://somegallery.com/gallery.php?gid=151
	 * http://somegallery.com/gallery.php?gid=152
	 * </pre>
	 * 
	 * It's also possible to use ranges with leading zeros:
	 * 
	 * <pre>
	 * http://somegallery.com/gallery.php?gid=[001-003]
	 * </pre>
	 * 
	 * The resulting URL's list:
	 * 
	 * <pre>
	 * http://somegallery.com/gallery.php?gid=001
	 * http://somegallery.com/gallery.php?gid=002
	 * http://somegallery.com/gallery.php?gid=003
	 * </pre>
	 * 
	 * @param url
	 *            The URL with a valid pattern or no pattern at all
	 * @return A MultiUrl list
	 * @throws InvalidPatternException
	 *             If an invalid pattern is found
	 */
	public final static List<MultiUrl> getMultiUrls(String url)
			throws InvalidPatternException {
		if (url == null) {
			throw new InvalidPatternException("The URL cannot be null");
		}

		int start = 0;
		int end = 0;

		MultiUrl multi = null;
		List<MultiUrl> multiUrls = new ArrayList<MultiUrl>();

		if ((start = url.indexOf(OPEN_BRACKET)) > -1) {
			if ((end = url.indexOf(CLOSE_BRACKET, start)) > -1) {
				String pattern = url.substring(start, end + 1);

				// The comma has the priority

				if (pattern.indexOf(COMMA) > -1) {
					String[] items = pattern.split(COMMA);

					for (String item : items) {
						if (item.indexOf(HYPHEN) > -1) {
							// It's a range item
							multiUrls.addAll(parseRange(item, pattern, url));
						} else {
							multi = new MultiUrl();

							String bareUrl = url.replace(pattern, DOLLAR);
							String matchedPattern = item.replaceAll(
									NON_NUMBER_REGEX, CommonConstants.EMPTY);

							multi.setUrl(bareUrl
									.replace(DOLLAR, matchedPattern));
							multi.setMatchedPattern(matchedPattern);

							multiUrls.add(multi);
						}
					}

					return multiUrls;
				} else {
					if (pattern.indexOf(HYPHEN) > -1
							&& pattern.indexOf(COMMA) == -1) {
						multiUrls.addAll(parseRange(pattern, pattern, url));

						return multiUrls;
					} else {
						throw new InvalidPatternException(
								"The URL pattern must have a comma or a hyphen");
					}
				}
			} else {
				throw new InvalidPatternException(
						"Couldn't find the ending pattern bracket ]");
			}
		} else {
			if ((end = url.indexOf(CLOSE_BRACKET, start)) > -1) {
				throw new InvalidPatternException(
						"Couldn't find the starting pattern bracket [");
			}

			multiUrls.add(saveNonPatternUrl(url));

			return multiUrls;
		}
	}

	private static MultiUrl saveNonPatternUrl(String pattern) {
		MultiUrl multi = new MultiUrl();

		multi.setUrl(pattern);
		multi.setMatchedPattern(CommonConstants.EMPTY);

		return multi;
	}

	private static List<MultiUrl> parseRange(String patternToParse,
			String fullPattern, String url) throws InvalidPatternException {
		int rangeFrom = 0;
		int rangeTo = 0;

		String[] ranges = patternToParse.split(HYPHEN);

		try {
			rangeFrom = Integer.valueOf(ranges[0].replaceAll(NON_NUMBER_REGEX,
					CommonConstants.EMPTY));
			rangeTo = Integer.valueOf(ranges[1].replaceAll(NON_NUMBER_REGEX,
					CommonConstants.EMPTY));

			String bareUrl = url.replace(fullPattern, DOLLAR);
			int size = rangeTo - rangeFrom;

			List<MultiUrl> multiUrls = new ArrayList<MultiUrl>();
			MultiUrl multi = null;

			for (int i = 0, gid = rangeFrom; i <= size && gid <= rangeTo; i++, gid++) {
				multi = new MultiUrl();
				multi.setUrl(bareUrl.replace(DOLLAR, getLeadingZeros(ranges[0],
						gid)
						+ String.valueOf(gid)));
				multi.setMatchedPattern(String.valueOf(gid));

				multiUrls.add(multi);
			}

			return multiUrls;
		} catch (Exception e) {
			throw new InvalidPatternException(
					"An error occured parsing the range pattern", e);
		}
	}

	private static String getLeadingZeros(String rangeFrom, int gid) {
		if (!rangeFrom.startsWith(OPEN_BRACKET_0)) {
			return CommonConstants.EMPTY;
		}

		rangeFrom = rangeFrom.replace(OPEN_BRACKET, CommonConstants.EMPTY);

		int digits = String.valueOf(gid).length();

		if (rangeFrom.length() == digits) {
			return CommonConstants.EMPTY;
		}

		int diff = rangeFrom.length() - digits;

		StringBuilder zeros = new StringBuilder();

		for (int i = 0; i < diff; i++) {
			zeros.append(ZERO);
		}

		return zeros.toString();
	}
}
